/*
 * @ : Copyright (c) 2021 Phytium Information Technology, Inc. 
 *  
 * SPDX-License-Identifier: Apache-2.0.
 * 
 * @Date: 2021-06-25 14:31:02
 * @LastEditTime: 2021-08-16 15:42:49
 * @Description:  This files is for 
 * 
 * @Modify History: 
 *  Ver   Who        Date         Changes
 * ----- ------     --------    --------------------------------------
 */
#include "trap.h"
#include "interrupt.h"
#include "exception.h"
#include <stdio.h>

#ifdef CONFIG_TARGET_ARMV8_AARCH64
#define aarch64_read_sysreg(reg) (          \
    {                                       \
        uint64_t val;                       \
        __asm__ __volatile__("mrs %0," #reg \
                             : "=r"(val));  \
        val;                                \
    })

static void print_EC_cause(uint64_t esr)
{
    uint32_t EC = (uint32_t)esr >> 26;

    switch (EC)
    {
    case 0b000000:
        printf("Unknown reason");
        break;
    case 0b000001:
        printf("Trapped WFI or WFE instruction execution");
        break;
    case 0b000011:
        printf("Trapped MCR or MRC access with (coproc==0b1111) that "
               "is not reported using EC 0b000000");
        break;
    case 0b000100:
        printf("Trapped MCRR or MRRC access with (coproc==0b1111) "
               "that is not reported using EC 0b000000");
        break;
    case 0b000101:
        printf("Trapped MCR or MRC access with (coproc==0b1110)");
        break;
    case 0b000110:
        printf("Trapped LDC or STC access");
        break;
    case 0b000111:
        printf("Trapped access to SVE, Advanced SIMD, or "
               "floating-point functionality");
        break;
    case 0b001100:
        printf("Trapped MRRC access with (coproc==0b1110)");
        break;
    case 0b001101:
        printf("Branch Target Exception");
        break;
    case 0b001110:
        printf("Illegal Execution state");
        break;
    case 0b010001:
        printf("SVC instruction execution in AArch32 state");
        break;
    case 0b011000:
        printf("Trapped MSR, MRS or System instruction execution in "
               "AArch64 state, that is not reported using EC "
               "0b000000, 0b000001 or 0b000111");
        break;
    case 0b011001:
        printf("Trapped access to SVE functionality");
        break;
    case 0b100000:
        printf("Instruction Abort from a lower Exception level, that "
               "might be using AArch32 or AArch64");
        break;
    case 0b100001:
        printf("Instruction Abort taken without a change in Exception "
               "level.");
        break;
    case 0b100010:
        printf("PC alignment fault exception.");
        break;
    case 0b100100:
        printf("Data Abort from a lower Exception level, that might "
               "be using AArch32 or AArch64");
        break;
    case 0b100101:
        printf("Data Abort taken without a change in Exception level");
        break;
    case 0b100110:
        printf("SP alignment fault exception");
        break;
    case 0b101000:
        printf("Trapped floating-point exception taken from AArch32 "
               "state");
        break;
    case 0b101100:
        printf("Trapped floating-point exception taken from AArch64 "
               "state.");
        break;
    case 0b101111:
        printf("SError interrupt");
        break;
    case 0b110000:
        printf("Breakpoint exception from a lower Exception level, "
               "that might be using AArch32 or AArch64");
        break;
    case 0b110001:
        printf("Breakpoint exception taken without a change in "
               "Exception level");
        break;
    case 0b110010:
        printf("Software Step exception from a lower Exception level, "
               "that might be using AArch32 or AArch64");
        break;
    case 0b110011:
        printf("Software Step exception taken without a change in "
               "Exception level");
        break;
    case 0b110100:
        printf("Watchpoint exception from a lower Exception level, "
               "that might be using AArch32 or AArch64");
        break;
    case 0b110101:
        printf("Watchpoint exception taken without a change in "
               "Exception level.");
        break;
    case 0b111000:
        printf("BKPT instruction execution in AArch32 state");
        break;
    case 0b111100:
        printf("BRK instruction execution in AArch64 state.");
        break;
    }
    printf("\n");
}

void ShowRegister(exception_frame *regs)
{
    printf("Execption:\n");
    printf("r00:0x%lx r01:0x%lx r02:0x%lx r03:0x%lx\r\n", regs->x0, regs->x1, regs->x2, regs->x3);
    printf("r04:0x%lx r05:0x%lx r06:0x%lx r07:0x%lx\r\n", regs->x4, regs->x5, regs->x6, regs->x7);
    printf("r08:0x%lx r09:0x%lx r10:0x%lx r11:0x%lx\r\n", regs->x8, regs->x9, regs->x10, regs->x11);
    printf("r12:0x%lx r13:0x%lx r14:0x%lx r15:0x%lx\r\n", regs->x12, regs->x13, regs->x14, regs->x15);
    printf("r16:0x%lx r17:0x%lx r18:0x%lx r19:0x%lx\r\n", regs->x16, regs->x17, regs->x18, regs->x19);
    printf("r20:0x%lx r21:0x%lx r22:0x%lx r23:0x%lx\r\n", regs->x20, regs->x21, regs->x22, regs->x23);
    printf("r24:0x%lx r25:0x%lx r26:0x%lx r27:0x%lx\r\n", regs->x24, regs->x25, regs->x26, regs->x27);
    printf("r28:0x%lx r29:0x%lx r30:0x%lx\r\n", regs->x28, regs->x29, regs->x30);
    printf("exc_type 0x%lx\r\n", regs->exc_type);
    printf("exc_esr 0x%lx\r\n", regs->exc_esr);
    printf("exc_sp 0x%lx\r\n", regs->exc_sp);
    printf("exc_spsr 0x%lx\r\n", regs->exc_spsr);
    printf("exc_elr 0x%lx\r\n", regs->exc_elr);
}

/**
 * When comes across an instruction which it cannot handle,
 * it takes the undefined instruction trap.
 *
 * @param regs system registers
 *
 * @note never invoke this function in application
 */
void SerrHandle(exception_frame *regs)
{
    uint64_t esr, far;
    uint64_t ec, iss;

    printf("error exception:\n");
    esr = aarch64_read_sysreg(esr_el1);
    far = aarch64_read_sysreg(far_el1);
    ec = (esr >> 26) & 0x3f;
    iss = (esr >> 0) & 0x1ffffff;
    printf("\nSError exception detected, ec:0x%lx iss:0x%lx far:0x%lx\r\n", ec, iss, far);
    print_EC_cause(esr);
    ShowRegister(regs);
    while (1)
    {
        /* code */
    }
}
#endif

#ifdef CONFIG_TARGET_ARMV8_AARCH32
/**
 * this function will show registers of CPU
 *
 * @param regs the registers point
 */
void ShowExceptionRegisters(struct ExceptionStack *regs)
{
    printf("Execption:\r\n");
    printf("r00:0x%08lx r01:0x%08lx r02:0x%08lx r03:0x%08lx\r\n", regs->r0, regs->r1, regs->r2, regs->r3);
    printf("r04:0x%08lx r05:0x%08lx r06:0x%08lx r07:0x%08lx\r\n", regs->r4, regs->r5, regs->r6, regs->r7);
    printf("r08:0x%08lx r09:0x%08lx r10:0x%08lx\r\n", regs->r8, regs->r9, regs->r10);
    printf("fp :0x%08lx ip :0x%08lx\r\n", regs->fp, regs->ip);
    printf("sp :0x%08lx lr :0x%08lx pc :0x%08lx\r\n", regs->sp, regs->lr, regs->pc);
    printf("cpsr:0x%08lx\r\n", regs->cpsr);
}

/**
 * An abort indicates that the current memory access cannot be completed,
 * which occurs during an instruction prefetch.
 *
 * @param regs system registers
 *
 * @note never invoke this function in application
 */
void PrfTrap(struct ExceptionStack *regs)
{
    printf("prefetch abort:\r\n");
    ShowExceptionRegisters(regs);
}
#endif

#define GIC_ACK_INTID_MASK 0x000003ff

int InterruptGetIrq(void);
void InterruptAck(int fiq_irq);

void SystemIrqHandler(int ir)
{
    void *param;
    IrqHandler isr_func;
    extern struct IrqDesc isr_table[];
    /* get interrupt service routine */
    isr_func = isr_table[ir].handler;
    if (isr_func)
    {
        /* Interrupt for myself. */
        param = isr_table[ir].param;
        /* turn to interrupt service routine */
        isr_func(ir, param);
    }

    /* end of interrupt */
    InterruptAck(ir);
}

void IrqHandleException(void *value)
{
    void *param;
    int ir;
    IrqHandler isr_func;
    extern struct IrqDesc isr_table[];
    (void)value;
    ir = InterruptGetIrq();
    if (ir == 1023)
    {
        /* Spurious interrupt */
        return;
    }
    /* get interrupt service routine */
    isr_func = isr_table[ir].handler;

    if (isr_func)
    {
        /* Interrupt for myself. */
        param = isr_table[ir].param;
        /* turn to interrupt service routine */
        isr_func(ir, param);
    }

    /* end of interrupt */
    InterruptAck(ir);
}
